/************************************
*  Copyright (C) 2010 Julien MOTTIN.
*  Contact: julien.mottin@gueydan.eu
*
*  This file is part of Gueydan.
*
*  Gueydan is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  Gueydan is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with Gueydan.  If not, see <http://www.gnu.org/licenses/>.
*
*************************************/
// Local
#include "mash.h"
#include "util.h"
#include "xmldico.h"

// QtXml
#include <QDomNode>
#include <QDomElement>
#include <QDomDocument>
#include <QDomNodeList>

Mash::Mash(const QString &iName, int iVersion, const Temperature &iGrainTemp)
    :BeerXMLObject(iName,iVersion)
    ,_GrainTemp(iGrainTemp)
    ,_MashSteps()
    ,_Notes("")
    ,_TunTemp(0.0)
    ,_SpargeTemp(0.0)
    ,_PH(0.0)
    ,_TunWeight(0.0)
    ,_TunSpecificHeat(0.0)
    ,_EquipAdjust(false)
{
    _MashSteps.clear();
}

Mash::Mash()
    :BeerXMLObject()
    ,_GrainTemp(0.0)
    ,_MashSteps()
    ,_Notes("")
    ,_TunTemp(0.0)
    ,_SpargeTemp(0.0)
    ,_PH(0.0)
    ,_TunWeight(0.0)
    ,_TunSpecificHeat(0.0)
    ,_EquipAdjust(false)
{
    _MashSteps.clear();
}

Mash::~Mash()
{
    // Clean list
    for (int i=0;i<_MashSteps.size();i++)
    {
        MashStep *pStep = _MashSteps[i];
        if (pStep)
        {
            pStep->Release();
            pStep = NULL;
        }
    }
}

bool Mash::operator==(const Mash &iOther) const
{
    return ((*this).BeerXMLObject::operator==(iOther) &&
            _GrainTemp == iOther._GrainTemp &&
            _MashSteps == iOther._MashSteps &&
            _Notes == iOther._Notes &&
            _TunTemp == iOther._TunTemp &&
            _SpargeTemp == iOther._SpargeTemp &&
            ComparePrecision(_PH,iOther._PH) &&
            _TunWeight == iOther._TunWeight &&
            ComparePrecision(_TunSpecificHeat,iOther._TunSpecificHeat) &&
            _EquipAdjust == iOther._EquipAdjust);
}

bool Mash::operator!=(const Mash &iOther) const
{
    return (!((*this)==iOther));
}

int Mash::CreateFromXMLNode(const QDomNode &iNode)
{
    int oRC = 1;
    if (!iNode.isNull())
    {
        QDomElement MashElement;

        // Case Document : check if doc has only one Yeast element
        if (iNode.isDocument())
        {
            QDomDocument MashDoc = iNode.toDocument();
            QDomNodeList MashList = MashDoc.elementsByTagName(MASH);
            if (MashList.length()==1)
                MashElement = MashList.item(0).toElement();
        }
        // Default : cast to element
        else
            MashElement = iNode.toElement();

        // Check element consistency
        if (!MashElement.isNull() && MASH==MashElement.tagName())
        {
            // Check existence of mandatory elements
            bool MashName = false;
            bool MashVersion = false;
            bool MashGrainTemp = false;
            bool MashSteps = false;

            QDomNode node = MashElement.firstChild();
            while (!node.isNull())
            {
                if (node.isElement())
                {
                    QDomElement elem = node.toElement();
                    QString name = elem.tagName();
                    QString text = elem.text();
                    if (name==MASH_NAME)
                    {
                        _Name = text;
                        MashName = true;
                    }
                    else if (name==MASH_VERSION)
                    {
                        bool ok = false;
                        int v = text.toInt(&ok);
                        if (ok)
                        {
                            MashVersion = true;
                            _Version = v;
                        }
                    }
                    else if (name==MASH_GRAINTEMP)
                    {
                        bool ok = false;
                        MashGrainTemp = true;
                        _GrainTemp.FromString(text,&ok);
                        if (!ok)
                        {
                            MashGrainTemp = false;
                            break;
                        }
                    }
                    else if (name==MASH_STEPS)
                    {
                        // Loop over MashSteps children
                        QDomNode stepNode = elem.firstChild();
                        while (!stepNode.isNull())
                        {
                            if (stepNode.isElement())
                            {
                                // Find MASHSTEP elements
                                QDomElement stepElem = stepNode.toElement();
                                QString stepName = stepElem.tagName();
                                if (stepName==MASHSTEP)
                                {
                                    // Parse MashStep
                                    MashStep *pStep = new MashStep();
                                    int rc = pStep->CreateFromXMLNode(stepElem);
                                    if (0==rc)
                                    {
                                        // Store step
                                        pStep->AddRef();
                                        _MashSteps.append(pStep);
                                        MashSteps = true;
                                    }
                                    // Clean
                                    pStep->Release();
                                }
                            }

                            // Iterate
                            stepNode = stepNode.nextSibling();
                        }
                    }
                    else if (name==MASH_NOTES)
                    {
                        _Notes = text;
                    }
                    else if (name==MASH_TUNTEMP)
                    {
                        _TunTemp.FromString(text);
                    }
                    else if (name==MASH_SPARGETEMP)
                    {
                        _SpargeTemp.FromString(text);
                    }
                    else if (name==MASH_PH)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                            _PH = v;
                    }
                    else if (name==MASH_TUNWEIGHT)
                    {
                        _TunWeight.FromString(text);
                    }
                    else if (name==MASH_TUNSPECIFICHEAT)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                            _TunSpecificHeat = v;
                    }
                    else if (name==MASH_EQUIPADJUST)
                    {
                        if (text.toLower()=="true")
                            _EquipAdjust = true;
                    }
                }

                // Iterate
                node = node.nextSibling();
            }

            // Update output
            if (MashName && MashVersion && MashGrainTemp && MashSteps)
            {
                oRC = 0;
            }
        }
    }

    return oRC;
}


int Mash::ToXMLNode(QDomNode &iParentNode) const
{
    int oRC = 1;

    // First, create valid element node
    QDomDocument doc = iParentNode.ownerDocument();
    QDomElement MashNode = doc.createElement(MASH);
    if (!MashNode.isNull())
    {
        int NbSteps = _MashSteps.size();
        if (!_Name.isEmpty() && _Version>0 && NbSteps>0)
        {
            QDomElement node;
            QDomText textNode;

            // Name
            node = doc.createElement(MASH_NAME);
            Q_ASSERT(!node.isNull());
            textNode = doc.createTextNode(_Name);
            Q_ASSERT(!textNode.isNull());
            node.appendChild(textNode);
            MashNode.appendChild(node);

            // Version
            node = doc.createElement(MASH_VERSION);
            Q_ASSERT(!node.isNull());
            textNode = doc.createTextNode(QString::number(_Version));
            Q_ASSERT(!textNode.isNull());
            node.appendChild(textNode);
            MashNode.appendChild(node);

            // Grain Temp
            node = doc.createElement(MASH_GRAINTEMP);
            Q_ASSERT(!node.isNull());
            textNode = doc.createTextNode(QString::number(_GrainTemp.Value(Celsius)));
            Q_ASSERT(!textNode.isNull());
            node.appendChild(textNode);
            MashNode.appendChild(node);

            // MashStep
            node = doc.createElement(MASH_STEPS);
            Q_ASSERT(!node.isNull());
            for (int i=0;i<NbSteps;i++)
            {
                _MashSteps[i]->ToXMLNode(node);
            }
            MashNode.appendChild(node);

            // Notes
            if (!_Notes.isEmpty())
            {
                node = doc.createElement(MASH_NOTES);
                Q_ASSERT(!node.isNull());
                textNode = doc.createTextNode(_Notes);
                Q_ASSERT(!textNode.isNull());
                node.appendChild(textNode);
                MashNode.appendChild(node);
            }

            // Tun temperature
            if (_TunTemp>0)
            {
                node = doc.createElement(MASH_TUNTEMP);
                Q_ASSERT(!node.isNull());
                textNode = doc.createTextNode(QString::number(_TunTemp.Value(Celsius)));
                Q_ASSERT(!textNode.isNull());
                node.appendChild(textNode);
                MashNode.appendChild(node);
            }

            // Sparge temperature
            if (_TunTemp>0)
            {
                node = doc.createElement(MASH_SPARGETEMP);
                Q_ASSERT(!node.isNull());
                textNode = doc.createTextNode(QString::number(_SpargeTemp.Value(Celsius)));
                Q_ASSERT(!textNode.isNull());
                node.appendChild(textNode);
                MashNode.appendChild(node);
            }

            // PH
            if (_PH>0)
            {
                node = doc.createElement(MASH_PH);
                Q_ASSERT(!node.isNull());
                textNode = doc.createTextNode(QString::number(_PH));
                Q_ASSERT(!textNode.isNull());
                node.appendChild(textNode);
                MashNode.appendChild(node);
            }

            // Tun Weight
            if (_TunWeight>0)
            {
                node = doc.createElement(MASH_TUNWEIGHT);
                Q_ASSERT(!node.isNull());
                textNode = doc.createTextNode(QString::number(_TunWeight.Value(Kilogram)));
                Q_ASSERT(!textNode.isNull());
                node.appendChild(textNode);
                MashNode.appendChild(node);
            }

            // Tun specific heat
            if (_TunSpecificHeat>0)
            {
                node = doc.createElement(MASH_TUNSPECIFICHEAT);
                Q_ASSERT(!node.isNull());
                textNode = doc.createTextNode(QString::number(_TunSpecificHeat));
                Q_ASSERT(!textNode.isNull());
                node.appendChild(textNode);
                MashNode.appendChild(node);
            }

            // EquipAdjust
            if (_EquipAdjust)
            {
                node = doc.createElement(MASH_EQUIPADJUST);
                Q_ASSERT(!node.isNull());
                textNode = doc.createTextNode("TRUE");
                Q_ASSERT(!textNode.isNull());
                node.appendChild(textNode);
                MashNode.appendChild(node);
            }

            // Update output
            QDomNode zeNode = iParentNode.appendChild(MashNode);
            if (!zeNode.isNull())
                oRC = 0;
        }
    }
    return oRC;
}
